/*
* Copyright 2010 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/

package org.etherframework.dependency
{
	import org.etherframework.core.IBridge;
	import org.etherframework.core.IControllerPart;
	import org.etherframework.core.ether_internal;
	import org.etherframework.core.events.InteractionEvent;
	
	import flash.events.Event;
	import flash.utils.Dictionary;
	
	use namespace ether_internal;
	
	[ExcludeClass]
	public class DependencyController implements IControllerPart
	{
		private var storage:DependencyStorage;
		
		private var storageCache:Dictionary;
		
		private var accessInfos:Dictionary;
		
		private var isInitialized:Boolean = false;
		
		public function DependencyController(tags:Array)
		{
			_tags = tags;
			accessInfos = new Dictionary();
			storage = new DependencyStorage(storageCache = new Dictionary());
		}
		
		public function initialize():void
		{
			if (isInitialized && !_bridge)
				return;
			
			var i:int;
			var j:int;
			var k:int;
			var tagsLen:int = _tags.length;
			var dependency:Dependency;
			var dependencyItems:Array;
			var dependencyItemsLen:int;
			var beans:Beans;
			var beansLen:int;
			var info:Array = [];
			var infoLen:int;
			var dependencyInfo:DependencyInfo;
			var evt:InteractionEvent;
			
			for (i = 0; i < tagsLen; ++i)
			{
				dependency = _tags[i];
				dependencyItems = dependency.items;
				dependencyItemsLen = dependencyItems.length;
				
				for (j = 0; j < dependencyItemsLen; ++j)
				{
					beans = dependencyItems[j];
					info = addItem(beans) as Array;
					infoLen = info.length;
					
					for (k = 0; k < infoLen; ++k)
					{
						dependencyInfo = info[k];
						accessInfos[dependencyInfo.name] = new AccessInfo(beans.access, beans.domain);
						bridge.addWire(dependencyInfo.item);
						bridge.listen(InteractionEvent.GET_DEPENDENCY + dependencyInfo.name, 
										getItemHandler, beans.access, beans.domain);
						
						evt = new InteractionEvent(InteractionEvent.DEPENDENCY_ADDED + dependencyInfo.name);
						evt.data = dependencyInfo;
						bridge.dispatch(evt, beans.access, beans.domain);
						
						evt = new InteractionEvent(InteractionEvent.DEPENDENCY_ADDED);
						evt.data = dependencyInfo;
						bridge.dispatch(evt, beans.access, beans.domain);
					}
					
					beans.dispatchEvent(new Event("ready"));
				}
			}
			
			isInitialized = true;
		}
		
		public function uninitialize():void
		{
			if (!isInitialized)
				return;
			
			var evt:InteractionEvent;
			var accessInfo:AccessInfo;
			
			for each(var item:DependencyInfo in storageCache)
			{
				accessInfo = accessInfos[item.name];
				evt = new InteractionEvent(InteractionEvent.DEPENDENCY_REMOVED + item.name);
				evt.data = item.name;
				bridge.dispatch(evt, accessInfo.access, accessInfo.domain);
			}
			
			accessInfos = null;
			isInitialized = false;
		}
		
		public function addItem(value:Object):Object
		{
			if (!value)
				return [];
			
			var result:Array = [];
			var items:Dictionary = Beans(value).getItems();
			var beanInfo:DependencyInfo;
			var beanItems:Array = [];
			
			for (var item:String in items)
			{
				beanInfo = new DependencyInfo(item, items[item], bridge.token);
				storage.addItem(beanInfo);
				result.push(beanInfo);
				
				if (isInitialized)
				{
					bridge.addWire(items[item]);
				}
			}
			
			return result;
		}
		
		public function removeItem(value:Object):Object
		{
			return null;
		}
		
		public function getItem(value:*):*
		{
			return storage.getItem(value);
		}
		
		private var _tags:Array;
		
		public function get tags():Array
		{
			return _tags;
		}
		
		private var _bridge:IBridge;
		
		public function get bridge():IBridge
		{
			return _bridge;
		}
		
		public function set bridge(value:IBridge):void
		{
			_bridge = value;
		}
		
		private function getItemHandler(event:Event):void
		{
			if (("data" in event) && ("result" in event))
			{
				var name:String = event["data"] as String;
				
				if (name)
				{
					event["result"] = getItem(name);
					event.preventDefault();
				}
			}
		}
	}
}

final class AccessInfo
{
	public var access:String;
	
	public var domain:String;
	
	function AccessInfo(access:String, domain:String)
	{
		this.access = access;
		this.domain = domain;
	}
}